'''
copyright: Copyright (C) 2015-2024, Wazuh Inc.

           Created by Wazuh, Inc. <info@wazuh.com>.

           This program is free software; you can redistribute it and/or modify it under the terms of GPLv2

type: integration

brief: These tests will check if the `min_full_scan_interval` option of the Vulnerability Detector module
       is working correctly. This option is located in its corresponding section of the `ossec.conf` file
       and allows to define the minimum time before performing a full scan even if the feed was updated. The extended
       behavior also checks that the full scan is performed as expected.

components:
    - vulnerability_detector

suite: general_settings

targets:
    - manager

daemons:
    - wazuh-modulesd

os_platform:
    - linux

os_version:
    - Arch Linux
    - Amazon Linux 2
    - Amazon Linux 1
    - CentOS 8
    - CentOS 7
    - CentOS 6
    - Ubuntu Focal
    - Ubuntu Bionic
    - Ubuntu Xenial
    - Ubuntu Trusty
    - Debian Buster
    - Debian Bullseye
    - Red Hat 8
    - Red Hat 7
    - Red Hat 6

references:
    - https://documentation.wazuh.com/current/user-manual/capabilities/vulnerability-detection/index.html
    - https://documentation.wazuh.com/current/user-manual/reference/ossec-conf/vuln-detector.html#min_full_scan_interval
    - https://github.com/wazuh/wazuh/issues/7749
    - https://github.com/wazuh/wazuh/issues/8727

tags:
    - settings
    - general_settings
'''
import pytest
from pathlib import Path

from wazuh_testing.constants.daemons import ANALYSISD_DAEMON, MODULES_DAEMON, SYSCHECK_DAEMON
from wazuh_testing.constants.paths.logs import WAZUH_LOG_PATH
from wazuh_testing.utils.callbacks import generate_callback
from wazuh_testing.utils.time import time_to_seconds
from wazuh_testing.tools.monitors.file_monitor import FileMonitor
from wazuh_testing.utils import mocking
from wazuh_testing.utils.db_queries import agent_db
from wazuh_testing.utils.configuration import (get_test_cases_data, load_configuration_template,
                                               update_configuration_template)
from wazuh_testing.modules.modulesd.vulnerability_detector import patterns as cb
from wazuh_testing.modules.modulesd.configuration import MODULESD_DEBUG
from wazuh_testing.modules.monitord.configuration import MONITORD_ROTATE_LOG
from . import (TEST_CASES_PATH, CONFIGURATIONS_PATH, custom_rhel_oval_feed_path, custom_rhel_json_feed_path,
               custom_nvd_json_feed_path)


pytest.skip("The tests will be deprecated, they test the old Vulnerability Detector.", allow_module_level=True)

# Variables
local_internal_options = {MODULESD_DEBUG: '2', MONITORD_ROTATE_LOG: '0'}
daemons_handler_configuration = {'daemons': [ANALYSISD_DAEMON, MODULES_DAEMON, SYSCHECK_DAEMON]}
pytestmark = [pytest.mark.server]

# Configuration and cases data
configurations_path = Path(CONFIGURATIONS_PATH, 'configuration_retry_interval.yaml')
test_cases_path = Path(TEST_CASES_PATH, 'cases_retry_interval.yaml')

# Test configurations
configuration_parameters, configuration_metadata, test_case_ids = get_test_cases_data(test_cases_path)
configurations = load_configuration_template(configurations_path, configuration_parameters, configuration_metadata)
systems = [metadata['system'] for metadata in configuration_metadata]

# Add the custom feeds path in configurations
tags_to_replace = ['CUSTOM_RHEL_OVAL_FEED_PATH', 'CUSTOM_RHEL_JSON_FEED_PATH', 'CUSTOM_NVD_JSON_FEED_PATH']
new_tags_values = [custom_rhel_oval_feed_path, custom_rhel_json_feed_path, custom_nvd_json_feed_path]
configurations = update_configuration_template(configurations, tags_to_replace, new_tags_values)


@pytest.mark.tier(level=0)
@pytest.mark.parametrize('test_configuration, test_metadata, agent_system',
                         zip(configurations, configuration_metadata, systems), ids=test_case_ids)
def test_retry_interval(test_configuration, test_metadata, agent_system, set_wazuh_configuration,
                        truncate_monitored_files, configure_local_internal_options, clean_cve_tables,
                        mock_agent_with_custom_system, mock_agent_packages, daemons_handler):
    '''
    description: Check if the `retry_interval ` option is working correctly. To do this,
                 it checks the `ossec.log` file for the message indicating that Vulnerability Detector will sleep before
                 attempting to scan the pending agents (forcing the retry interval action). After this, the DBs are
                 synchronized and the scan (log) is checked to ensure that it is performed normally.

    test_phases:
        - setup:
            - Set a custom Wazuh configuration.
            - Configure custom local_internal_options.
            - Truncate wazuh logs.
            - Mock an agent with packages.
            - Update sync info a force the full scan.
            - Restart wazuh-modulesd daemon to apply configuration changes.
        - test:
            - Delete one package from the agent to avoid checksum sync validation.
            - Update the sync_info data to force the retry interval scan.
            - Check the retry interval event in log.
            - Add again the package and update the sync_info data to force the retry interval scan.
            - Search analyzing OVAL vulnerabilities event (scan is running successfully).
        - teardown:
            - Truncate wazuh logs.
            - Restore initial configuration,and local_internal_options.
            - Remove the mocked agent.
            - Restart modulesd to apply configuration.

    wazuh_min_version: 4.3.0

    tier: 0

    parameters:
        - test_configuration:
            type: dict
            brief: Wazuh configuration data. Needed for set_wazuh_configuration fixture.
        - test_metadata:
            type: dict
            brief: Wazuh configuration metadata.
        - agent_system:
             type: str
             brief: System to set to the mocked agent.
        - set_wazuh_configuration:
            type: fixture
            brief: Set the wazuh configuration according to the configuration data.
        - configure_local_internal_options:
            type: fixture
            brief: Set local_internal_options configuration.
        - truncate_monitored_files:
            type: fixture
            brief: Truncate the log files at the end of the testing case.
        - clean_cve_tables:
            type: fixture
            brief: Clean all the CVE tables before and after running the test.
        - mock_agent_with_custom_system:
            type: fixture
            brief: Create a mocked agent with specific system. Needed to force the scan according to the inserted feeds.
        - mock_agent_packages:
            type: fixture
            brief: Add a mocked packages in the agent DB.
        - daemons_handler:
            type: fixture
            brief: Restart the wazuh-modulesd daemon.

    assertions:
        - If the retry interval message is showed.
        - If the scan occurs after the second retry.

    input_description:
        - The `test_retry_interval.yaml` file provides the module configuration for this test.

    expected_output:
        - 'Going to sleep \\d+ seconds before retrying pending agents'
        - f"Analyzing OVAL vulnerabilities for agent '{agent_id}'"
    '''
    log_monitor = FileMonitor(WAZUH_LOG_PATH)
    agent_id = mock_agent_with_custom_system

    # Delete one package from the agent to avoid checksum sync validation
    agent_db.delete_package(package=mock_agent_packages[0], agent_id=agent_id)

    # Update the sync_info data to force the retry interval scan
    agent_db.update_sync_info(last_completion=1, last_attempt=2, agent_id=agent_id)

    # Check the retry interval event in log
    retry_interval = str(time_to_seconds(test_metadata['retry_interval']))
    log_monitor.start(timeout=20, callback=generate_callback(regex=cb.RETRY_INTERVAL_LOG,
                                                             replacement={"seconds_number": retry_interval}))
    assert log_monitor.callback_result is not None, f"Expected '{cb.RETRY_INTERVAL_LOG}' event was not found."

    # Add again the package
    mocking.insert_mocked_packages(num_packages=1, agent_id=agent_id)

    # Update the sync_info data to force the retry interval scan
    agent_db.update_sync_info(last_completion=2, last_attempt=2, agent_id=agent_id)

    # Search analyzing OVAL vulnerabilities event
    log_monitor.start(timeout=20, callback=generate_callback(regex=cb.ANALIZING_OVAL_VULNS,
                                                             replacement={"agent_id": agent_id}))
    assert log_monitor.callback_result is not None, f"Expected '{cb.ANALIZING_OVAL_VULNS}' event was not found."


@pytest.mark.tier(level=0)
@pytest.mark.parametrize('test_configuration, test_metadata, agent_system',
                         zip(configurations, configuration_metadata, systems), ids=test_case_ids)
def test_retry_interval_max_retries(test_configuration, test_metadata, agent_system, set_wazuh_configuration,
                                    truncate_monitored_files, configure_local_internal_options, clean_cve_tables,
                                    mock_agent_with_custom_system, mock_agent_packages, daemons_handler):
    '''
    description: Check if after exceeding the maximum number of retries for a scan (`retry_interval`) the
                 corresponding message is displayed indicating that the software could not be obtained for the agent
                 and that it will be retried at the next scan (waiting for its time interval).

    test_phases:
        - setup:
            - Set a custom Wazuh configuration.
            - Configure custom local_internal_options.
            - Truncate wazuh logs.
            - Mock an agent with packages.
            - Update sync info a force the full scan.
            - Restart wazuh-modulesd daemon to apply configuration changes.
        - test:
            - Delete one package from the agent to avoid checksum sync validation.
            - Update the sync_info data to force the retry interval scan.
            - Add again the package and update the sync_info data to force the retry interval scan.
            - Wait for all retries interval events (4 times).
            - Check the event: could not obtain software for that agent.
        - teardown:
            - Truncate wazuh logs.
            - Restore initial configuration,and local_internal_options.
            - Remove the mocked agent.
            - Restart modulesd to apply configuration.

    wazuh_min_version: 4.3.0

    tier: 0

    parameters:
        - test_configuration:
            type: dict
            brief: Wazuh configuration data. Needed for set_wazuh_configuration fixture.
        - test_metadata:
            type: dict
            brief: Wazuh configuration metadata.
        - agent_system:
             type: str
             brief: System to set to the mocked agent.
        - set_wazuh_configuration:
            type: fixture
            brief: Set the wazuh configuration according to the configuration data.
        - truncate_monitored_files:
            type: fixture
            brief: Truncate the log files at the end of the testing case.
        - clean_cve_tables:
            type: fixture
            brief: Clean all the CVE tables before and after running the test.
        - mock_agent_with_custom_system:
            type: fixture
            brief: Create a mocked agent with specific system. Needed to force the scan according to the inserted feeds.
        - mock_agent_packages:
            type: fixture
            brief: Add a mocked packages in the agent DB.
        - daemons_handler:
            type: fixture
            brief: Restart the wazuh-modulesd daemon.

    assertions:
        - If the retry interval messages are showed (4 times).
        - If the message is displayed indicating failure to obtain the agent software, and that it will wait for the
          next scan.

    input_description:
        - The `test_retry_interval.yaml` file provides the module configuration for this test.

    expected_output:
        - 'Going to sleep \\d+ seconds before retrying pending agents'
        - f"The software of the agent '{agent_id}' could not be obtained after {num_attemps} attempts. Skipping agent
          until the next scan."
    '''
    log_monitor = FileMonitor(WAZUH_LOG_PATH)
    agent_id = mock_agent_with_custom_system

    # Delete one package from the agent to avoid checksum sync validation
    agent_db.delete_package(package=mock_agent_packages[0], agent_id=agent_id)

    # Update the sync_info data to force the retry interval scan
    agent_db.update_sync_info(last_completion=1, last_attempt=2, agent_id=agent_id)

    # Check the retry interval event in log
    retry_interval = str(time_to_seconds(test_metadata['retry_interval']))
    log_monitor.start(timeout=20, accumulations=4,
                      callback=generate_callback(regex=cb.RETRY_INTERVAL_LOG,
                                                 replacement={"seconds_number": retry_interval}))
    assert log_monitor.callback_result is not None, f"Expected '{cb.RETRY_INTERVAL_LOG}' event was not found."

    # Check the event: could not obtain software for that agent
    log_monitor.start(timeout=40, callback=generate_callback(regex=cb.OBTAINING_SOFTWARE_FAILURE_LOG,
                                                             replacement={"agent_id": agent_id}))
    assert log_monitor.callback_result is not None, f"Expected '{cb.OBTAINING_SOFTWARE_FAILURE_LOG}' event not found."
